# A GameObject is just about any visible character on the screen
# blocks attribute sets whether you can walk on it or not
# fighter attribute requires a Fighter object, this gives the object hp / attackable /etc
# ai attribute lets monsters walk / attack / etc
# use_func is for items, if you make an item gameobject you need a use_func to make the item do something


import libtcodpy as libtcod
import Messages
import math

class GameObject:
    def __init__(self, gamestate, x, y, char, name, color, blocks=False, fighter=None, ai=None, use_func=None):
        self.x = x
        self.y = y
        self.char = char
        self.name = name
        self.color = color
        self.blocks = blocks
        self.gamestate = gamestate
        self.fighter = fighter
        self.ai = ai
        self.use_func = use_func
        if self.fighter:
            self.fighter.owner = self
        if self.ai:
            self.ai.owner = self
        if self.use_func:
            self.use_func.owner = self
        
    def move(self, dx, dy):
        if not self.gamestate.map.is_blocked(self.x + dx, self.y + dy):
            self.x += dx
            self.y += dy
            self.check_underfoot()
    
    def move_towards(self, target_x, target_y):
        dx = target_x - self.x
        dy = target_y - self.y
        distance = math.sqrt(dx**2 + dy**2)
        
        #normalize, preserve direction
        dx = int(round(dx / distance))
        dy = int(round(dy / distance))
        self.move(dx, dy)
        
    def distance_to(self, other):
        dx = other.x - self.x
        dy = other.y - self.y
        return math.sqrt(dx ** 2 + dy ** 2)
    
    def check_underfoot(self):
        for game_object in self.gamestate.game_objects:
            func = game_object.use_func
            if func is not None:
                if game_object.x == self.x and game_object.y == self.y:
                    Messages.message('You stepped on an item. Trying to use it')
                    func(self.gamestate, self)
                    self.gamestate.game_objects.remove(game_object)
    def draw(self):
        if libtcod.map_is_in_fov(self.gamestate.fov_map, self.x, self.y):
            libtcod.console_set_foreground_color(self.gamestate.con, self.color)
            libtcod.console_put_char(self.gamestate.con, self.x, self.y, self.char, libtcod.BKGND_NONE)
            
    def send_to_back(self):
        #prevents monsters from being drawn on top of each other
        self.gamestate.game_objects.remove(self)
        self.gamestate.game_objects.insert(0, self)
        
    def clear(self):
        libtcod.console_put_char(self.gamestate.con, self.x, self.y, ' ', libtcod.BKGND_NONE)
        
class Fighter:
    def __init__(self, hp, defense, power, death_func=None):
        self.max_hp = hp
        self.hp = hp
        self.defense = defense
        self.power = power
        self.death_func = death_func
        
    def take_damage(self, damage):
        if damage > 0:
            self.hp -= damage
            if self.hp <= 0:
                func = self.death_func
                if func is not None:
                    func(self.owner.gamestate, self.owner)
            
    def heal(self, amount):
        available = self.max_hp - self.hp
        if amount > available:
            self.hp += available
        else:
            self.hp += amount
        
    def attack(self, target):
        damage = self.power - target.fighter.defense
        if damage > 0:
            Messages.message(self.owner.name + ' attacks ' + target.name + ' for ' + str(damage) + ' hit points.')
            target.fighter.take_damage(damage)
        else:
            print self.owner.name + ' attacks but it has no effect.'
        
        
class BasicMonster:
    def take_turn(self):
        monster = self.owner
        if libtcod.map_is_in_fov(monster.gamestate.fov_map, monster.x, monster.y):
            if monster.distance_to(monster.gamestate.player) >= 2:
                monster.move_towards(monster.gamestate.player.x, monster.gamestate.player.y)
            elif monster.gamestate.player.fighter.hp > 0:
                monster.fighter.attack(monster.gamestate.player)